open Parser
open Lexer
open Types
open Interpreter
open Compiler

module type ICONTAINER =
sig
	val lazyML: unit -> unit
end;;

module CONTAINER : ICONTAINER =
struct
	
	(* AGGIUNTA DELLA VARIABILE V NELL'ENVIRONMENT ENV *)
	let rec addEnv(v, env) = let r = !env in r := Types.MCons(v, ref (!(!env)))

	(* FUNZIONE DI STAMPA DEI VALORI *)
	let rec toString stack = (match stack with
				Types.Int x -> string_of_int(x)
			| Types.Bool x -> string_of_bool(x)
			| Types.Bfun x -> "bfun"^x
			| Types.Clos x -> "clos"
			| Types.Cons (e1, e2) -> "cons(" ^ toString e1 ^ "," ^ toString e2 ^")"
			| Types.StackRef x -> "ref (" ^ toString !x ^ ")"
			| Types.Sus _ -> "sus"
			| Types.StackNull ->"null")

	(* FUNZIONI PER LA STAMPA CON VALUTAZIONE DI ESPRESSIONI E LISTE *)
	let rec print stack = (match stack with
				Types.Int x -> print_string (string_of_int(x))
			| Types.Bool x -> print_string(string_of_bool(x))
			| Types.Bfun x -> print_string("bfun"^x)
			| Types.Clos x -> print_string ("clos")
			| Types.Cons (e1, e2) -> print_string "["; print e1; printCons e2;print_string "]";
			| Types.StackRef x -> print !x
			| Types.Sus(is, env) -> print(INTERPRETER.eval(Types.MCons((Types.Ldsus is), ref (Types.MCons(Types.End, ref Types.MNil))), env))
			| Types.StackNull ->print_string (" null "))
	and printCons stack = (match stack with
				Types.Int x -> print_string "."; print_string (string_of_int(x))
			| Types.Bool x ->print_string "."; print_string(string_of_bool(x))
			| Types.Bfun x ->print_string "."; print_string("bfun"^x)
			| Types.Clos x ->print_string "."; print_string ("clos")
      		| Types.Cons (e1, e2) ->print_string ";"; print e1; printCons e2;
			| Types.StackRef x -> printCons !x
			| Types.Sus(is, env) -> printCons(INTERPRETER.eval(Types.MCons((Types.Ldsus is), ref (Types.MCons(Types.End, ref Types.MNil))), env))
			| Types.StackNull ->print_string (""))
		
	(* STAMPA DEL CONTENUTO DELL'ENVIRONMENT *)
	let rec envToString env = match env with
			Types.MNil -> print_string "fine\n"
		| Types.MCons(x, r) -> print_string ((toString x)^"\n"); envToString !r
	
	(* FUNZIONE DI STAMPA DEI VALORI *)
	let printTitle = fun () -> print_string "*********************************************************\n";
							   print_string "*                                                       *\n";							   
							   print_string "*              ML Call-By-Need v.1.1                    *\n";
							   print_string "*        Bianco Leonardo - Grillo Pierluigi             *\n";
							   print_string "*          Panvini Paolo - Turin Erica                  *\n";
							   print_string "*                                                       *\n";
							   print_string "*********************************************************\n\n"

	(* FUNZIONE Di STARTUP *)
	let lazyML = function () ->
				(printTitle();print_string"$ "; let input = ref (read_line()) and evGlobal = ref Types.MNil and nlistGlobal = ref [] in
					(while !input <> "quit" do
							(try
								let ast = Parser.main Lexer.token (Lexing.from_string !input) in
								(* COMPILAZIONE E INTERPRETAZIONE DI UN'ESPRESSIONE *)
								(match ast with Ast.Exp e -> print_string (toString((INTERPRETER.eval(!(COMPILER.compile(e,!nlistGlobal)), evGlobal)))^"\n")

								(* COMPILAZIONE E INTERPRETAZIONE DI UNA DEFINIZIONE *)
									| Ast.Def (x, e) ->
											let ris = INTERPRETER.eval(!(COMPILER.compile(e,!nlistGlobal)), evGlobal) in
											(addEnv(ris, ref evGlobal);
												nlistGlobal:= (x:: (!nlistGlobal));
												print_string ((toString(ris))^"\n"))

								(* COMPILAZIONE E INTERPRETAZIONE DI UNA DEFINIZIONE RICORSIVA *)
									| Ast.Defrec (x, e) ->
											(nlistGlobal:= (x:: (!nlistGlobal));
												let ind = ref Types.StackNull in(
													addEnv(Types.StackRef ind, ref evGlobal);
													let ris = INTERPRETER.eval(!(COMPILER.compile(e,!nlistGlobal)), evGlobal) in
													( ind := ris;
														print_string (toString(ris) ^"\n"))
												))

								(* COMPILAZIONE, INTERPRETAZIONE E STAMPA DI UN'ESPRESSIONE *)
									| Ast.Print e -> (print((INTERPRETER.eval(!(COMPILER.compile(e,!nlistGlobal)), evGlobal)));(print_string"\n"))
								)

							(* ECCEZIONI *)
							with COMPILER.Comp_Exception x -> print_string (x^"\n")
							   | INTERPRETER.Eval_Exception x -> print_string (x^"\n")
							   | _ -> print_string "Parsing Error\n");
							
							(* GESTIONE DEL PROMPT E DELL'INPUT *)
							print_string"\n$ ";
							input := read_line()
						done
					)
				)
end;;

CONTAINER.lazyML();;

